/* -*-  Mode:C; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 1999, 2000 Massachusetts Institute of Technology.
 *
 * This software is being provided by the copyright holders under the GNU
 * General Public License, either version 2 or, at your discretion, any later
 * version. (See the file `LICENSE' in the Linux source distribution.)
 *
 * This software is provided "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The entire risk as
 * to the quality and performance of the program is with you. See the GNU
 * General Public License for more details.
 *
 * The name and trademarks of copyright holders may NOT be used in 
 * advertising or publicity pertaining to the software without specific, 
 * written prior permission. Title to copyright in this software and any 
 * associated documentation will at all times remain with copyright 
 * holders. See the file CMAUTHORS which should have accompanied this software 
 * for a list of all copyright holders. 
 * 
 * Particular files in this distribution may be derived from previously
 * copyrighted software. This copyright applies only to those changes made by
 * the copyright holders listed in the CMAUTHORS file.
 */
/*
 * An implementation of the Congestion Manager (CM) for Linux.
 * Core CM declarations.
 */
#ifndef _CM_H
#define _CM_H

#include <linux/config.h>
#include <linux/net.h>
#include <asm/spinlock.h>
#include <linux/timer.h>

/* Srini - I believe these are in later kernels... I have copied these
   here from ip_masq.h */

#ifdef __SMP__
#define spin_lock_bh(lock)      do { start_bh_atomic(); spin_lock(lock); \
                                        } while (0)
#define spin_unlock_bh(lock)    do { spin_unlock(lock); end_bh_atomic(); \
                                        } while (0)
#define read_lock_bh(lock)      do { start_bh_atomic(); read_lock(lock); \
                                        } while (0)
#define read_unlock_bh(lock)    do { read_unlock(lock); end_bh_atomic(); \
                                        } while (0)
#define write_lock_bh(lock)     do { start_bh_atomic(); write_lock(lock); \
                                        } while (0)
#define write_unlock_bh(lock)   do { write_unlock(lock); end_bh_atomic(); \
                                        } while (0)
#else
#define spin_lock_bh(lock)      start_bh_atomic()
#define spin_unlock_bh(lock)    end_bh_atomic()
#define read_lock_bh(lock)      start_bh_atomic()
#define read_unlock_bh(lock)    end_bh_atomic()
#define write_lock_bh(lock)     start_bh_atomic()
#define write_unlock_bh(lock)   end_bh_atomic()
#endif 

#define CM_TABLESIZE 511

#define CM_DECR_SHIFT 1		/* multiplicative decrease factor = 2 */
#define CM_RTTSHIFT   3		/* 1/8 gain on current sample */
#define CM_RTTDEVSHIFT 2        /* 1/4 gain on current mean dev sample */
#define CM_MAX_RTO 10000        /* rto in jiffies; currently 10s */

/* lossmode in cm_update() */
#define CM_NOLOSS     0
#define CM_PERSISTENT 1
#define CM_TRANSIENT  2
#define CM_ECN        3

/* ALF/BUF options, agrees with cmappl/test/cmappl.h*/
#define CM_NO_BUF_ALF 		0        //default
#define CM_BUF 	      		1            	
#define CM_ALF 	      		2		

/*CM rate callback enable must be different than above set*/
#define CM_NO_RATE_CALLBACK 	10	//default
#define CM_RATE_CALLBACK 	11

/* CM cong ctl algorithm types */
#define AIMD 0
#define BINOMIAL 1

#include <asm/ioctls.h>

#define SIOCCMNOTIFY 	 SIOCPROTOPRIVATE
#define SIOCCMUPDATE  	 SIOCPROTOPRIVATE+1
#define SIOCCMREQUEST	 SIOCPROTOPRIVATE+2
#define SIOCCMREADY   	 SIOCPROTOPRIVATE+3
#define SIOCCMQUERY  	 SIOCPROTOPRIVATE+4
#define SIOCCMBULKQUERY  SIOCPROTOPRIVATE+5
#define SIOCCMSETCONGALG SIOCPROTOPRIVATE+6
#define SIOCCMGETMFLOW	 SIOCPROTOPRIVATE+7
#define SIOCCMSETMFLOW	 SIOCPROTOPRIVATE+8
#define SIOCCMGETTHRESHM SIOCPROTOPRIVATE+9
#define SIOCCMSETTHRESHM SIOCPROTOPRIVATE+10
#define SIOCCMGETASSOC	 SIOCPROTOPRIVATE+11
#define SIOCCMSETASSOC	 SIOCPROTOPRIVATE+12
#define SIOCCMBULKNOTIFY SIOCPROTOPRIVATE+13
#define SIOCCMBULKUPDATE SIOCPROTOPRIVATE+14
#define SIOCCMBULKREQUEST SIOCPROTOPRIVATE+15
#define SIOCCMDELASSOC	 SIOCPROTOPRIVATE+17
#define SIOCCMCONGPARAM	 SIOCPROTOPRIVATE+18

#define CM_REQUEST_INACTIVE	-1
#define CM_REQUEST_ACTIVE	 1
#define CM_REQUEST_GRANTED 	 0

#define CM_LOG_SIZE 8000
#define CMLOG_RESTART		1
#define CMLOG_INCREASE		2
#define CMLOG_DECREASE		3

struct cm_macroflow;
struct cm_flow;
struct cm_congctl;
struct cm_sched;
struct cm_prober;
struct cm_update;
struct cm_query;

typedef struct cm {
	int num_mflows;		/* number of active macroflows */
	rwlock_t mflowtab_lock; /* for additions/deletions to mflowtab */
	struct cm_macroflow *mflowtab[CM_TABLESIZE]; /* hash table of mflows */
} cm_t;

typedef struct cm_mflow_key {
	/* what we use to match a flow to mflow initially */
	__u32 dst;
} cm_mflow_key_t;

typedef struct cm_macroflow {
	__u32 xmittime;		/* soonest that one of our flows can xmit */
	__u32 id;		/* macroflow id */
	rwlock_t flows_lock;	/* for additions/deletions to flows */
	struct cm_flow *flows;	/* all the flows on this macroflow */
	int nflows;		/* number of active flows */
	int mtu;		/* path mtu */
	int pending;		/* how many flows have pending set */
	struct timer_list cm_restart_timer;/* RTO idleness ==> go into slow start */
	struct cm_sched *sched;	/* scheduler */
	struct cm_congctl *congctl; 	/* congestion controller */
	struct cm_prober *prober; 	/* probing protocol */

	struct cm_mflow_key key;

	struct cm_macroflow* next; 	/* next/prev element in */
	struct cm_macroflow* prev; 	/* table of macroflows */
} cm_macroflow_t;


typedef struct cm_flow {
	int cmid;		/* congestion mgr id */
	struct sock *sk;	/* backpointer to sock */
	cm_macroflow_t *mflow;	/* backpointer to macroflow */
	int type;           	/* CM_BUF/CM_ALF flow */
	int do_callback;     	/* CM_RATE_CALLBACK/CM_NO_RATE_CALLBACK */
	float downthreshmult;	/* multiplier for lower bound for 
				   rate callback */
	float upthreshmult;	/* multiplier for upper bound for 
				   rate callback */
	double lastrate;	/* last rate reported wrt thresholds */
	int pending;		/* is there a transmission request pending? */
	int ratechange;		/* is there a rate change pending? */
	__u32 reqtime;		/* if so, when was it made? */
	__u32 inflight;		/* inflight data estimate for this flow */
	struct sock *peersk;	/* ptr to peer sock;
				   for data sock == control sock 
				   for control sock == data sock */
	struct timer_list cm_update_timer;
				/* timer for the flow;
				   currently used to detect inactivity on
				   flow based on cm_update times */
	struct timer_list cm_request_timer;
				/* flow did not use request granted */
	int pid;		/* process id that owns this sock */
	int fd;			/* fd corresponding to this sock */
	struct sk_buff *data;   /* add ptr to skbuf here */
	void (*cmapp_send) (struct cm_flow *);
	void (*cmapp_update) (double bw, double srtt, double rttdev);
	struct cm_flow *next;	/* thread through flows for easy scheduling*/
	struct cm_flow *prev;
	int bulkcount;		/* count of socks affiliated with this 
				   ctrl sock */
	struct cm_flow *bulknext; /* thread through flows for easy bulk ops */
	struct cm_flow *bulkprev;
} cm_flow_t;

/* 
 * Congestion controller.  Implements TCP-like AIMD congestion control.
 */
typedef struct cm_congctl_ops {
	void (*update) 	(struct cm_congctl *cmcc, int nsent, int nrecd, 
			 __u32 rtt,  int lossmode);
	void (*query)	(struct cm_congctl *cmcc, double *bw, double *srtt,
			 double *rttdev);
	void (*notify)	(struct cm_congctl *cmcc, int nsent);
	void (*wakeup)	(struct cm_congctl *cmcc);
} cm_congctl_ops_t;

typedef struct cm_congctl_state {
	/* this should really be a union of different sets i.e.
	   tcp_state, equation_friendly_state, rate_ctl_state
	   for different cong. controllers... or maybe just a pointer
	   to void* that each cong. ctler casts to the correct type (yuck)
	*/
	int size;		/* packet size to use */
	int outstanding;	/* number of "unaccounted-for" bytes */
	int last_size;		/* last xmit size */
	__u32 last_time;	/* last xmit time */
	int srtt;		/* smoothed RTT estimate */
	int rttdev;		/* linear RTT deviation */
        int rto;                /* Retx timeout */
	double rate;		/* estimated rate */
	__u32 rttmin;		/* used by expo aging algorithm */
	__u32 last_loss;	/* when did last rate reduction occur? */
	int do_aging;		/* do aging or not */

	int cwnd;		/* congestion window */
	int ssthresh;		/* slow start threshold */
	int backoff;		/* expo aging variable: how many half-lifes? */
	__u32 last_backoff;	/* expo aging variable: time of last backoff */
} cm_congctl_state_t;

typedef struct cm_congctl {
	spinlock_t ccstate_lock;/* simple lock for state
				   no multiple readers here */
	cm_congctl_state_t state; 	/* current congestion state */
	cm_congctl_ops_t ops; 		/* congestion control algorithms */
	int active;			/* congctl is currently actively
					   processing info */
	cm_macroflow_t *mflow; 		/* pointer back to macroflow */
        int congctl_type;               /* type = 0 => AIMD, 1=> BINOMIAL */
#ifdef CONFIG_CM_BINOMIAL
	float congctl_param;            /* binomial congestion ctrl parameter*/
#endif
} cm_congctl_t;

/* 
 * Scheduler
 */
typedef struct cm_sched_ops {
	void 	(*schedule) 	(struct cm_sched *cms);
	double 	(*query_share)	(struct cm_sched *cms, cm_flow_t *flow);
	void 	(*delete_flow)	(struct cm_sched *cms, cm_flow_t *flow);
	void	(*notify)	(struct cm_sched *cms, cm_flow_t *flow, 
				 int nsent);
} cm_sched_ops_t;

typedef struct cm_sched_state {	
	/* this should also be a union like cong_ctl_state */
	cm_flow_t *lastflow;	/* ptr to last scheduled flow */
} cm_sched_state_t;

typedef struct cm_sched {
	spinlock_t sched_lock;/* simple lock for scheduler access
				   this code should be non-reentrent */
	cm_sched_state_t state;	/* current congestion state */
	cm_sched_ops_t ops; 	/* congestion control algorithms */
	cm_macroflow_t *mflow; 	/* pointer back to macroflow */
} cm_sched_t;

/*
 * Probing system
 */

typedef struct cm_prober {

} cm_prober_t;

typedef struct cm_update {
	int	nrecd;
	int 	nloss;
	int 	lossmode;
	unsigned int rtt;
} cm_update_t;

typedef struct cm_query {
	double rate;
	double srtt;
	double rttdev;
	int ratechange;
} cmquery;

typedef struct cm_fdquery {
	int fd;
	cmquery cmquery;
	int ready;
} cmfdquery;

typedef struct cm_thresh {
	float downthreshmult;	/* multiplier to compute lower threshold */
	float upthreshmult;	/* multiplier to compute upper threshold */
} cmthresh;

/* Get cmid, flow and mflow from sock */
#define SK_CM_ID(sk) ((sk)->tp_cm4->cmid)
#define SK_CM_FLOW(sk) ((sk)->tp_cm4)
#define SK_CM_MFLOW(sk) ((sk)->tp_cm4->mflow)
#define SK_CM_PEERSK(sk) ((sk)->tp_cm4->peersk)

extern unsigned long volatile jiffies;

#define CMV4_HTABLE_SIZE     MAX_INET_PROTOS
extern struct proto cm_prot;
extern struct proto_ops         inet_cm_ops;

extern void cm_boot(void);
extern void cm_wakeup(void *);

/* CM API */
extern int cm_open(struct sock *sk);
extern int cm_request(cm_flow_t *flow);
extern void cm_update(cm_flow_t *flow, int nrecd, int nlost, int lossmode, 
		      __u32 rtt);

/* need to figure out if this is the correct place for timeouts for
 * flows and macroflows; cm_update_timeout => flow did not get update
 * for a while; cm_request_timeout => flow did not use the request
 * granted; cm_restart_timeout => mflow idle for very long, so restart
 * */

extern void cm_update_timeout(unsigned long);
extern void cm_request_timeout(unsigned long);
extern void cm_restart_timeout(unsigned long);

extern void cm_notify(cm_flow_t *flow, int nsent);
extern void cm_close(cm_flow_t *flow);
extern int cm_ready(cm_flow_t *flow);
extern void cm_query(cm_flow_t *flow, struct cm_query *cmquery);
extern int cm_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg);
extern void cm_cm_set_cong_param(cm_flow_t *flow, float k);
extern int cm_ratechange(cm_flow_t *flow);

extern void cmapp_send_wakeup(cm_flow_t *flow);

/* Congestion controller functions */
extern cm_congctl_t *cmcc_init(cm_macroflow_t *mflow);
extern void cmcc_free(cm_macroflow_t *mf);
extern void cmcc_update(cm_congctl_t *cmcc, int nsent, int nrecd, 
			__u32 rtt, int lossmode);
extern void cmcc_query(cm_congctl_t *cmcc, double *bw, double *srtt, 
		       double *rttdev);
extern void cmcc_notify(cm_congctl_t *cmcc, int nsent);
extern void cmcc_wakeup(cm_congctl_t *cmcc);
extern void cmcc_restart(cm_congctl_t *cmcc);
//extern void cmtcpcc_update_rate(cm_congctl_t *cmtcpcc);

/* CM scheduler functions */
extern cm_sched_t *cms_init(cm_macroflow_t *mflow);
extern void cms_free(cm_macroflow_t *mf);
extern void cms_schedule(cm_sched_t *cms);
extern double cms_query_share(cm_sched_t *cms, cm_flow_t *flow);
extern void cms_delete_flow(cm_sched_t *cms, cm_flow_t *flow);
extern void cms_notify(cm_sched_t *cms, cm_flow_t *flow, int nsent);

/* CM macroflow and flow functions */
extern cm_macroflow_t *cm_find_mflow(struct sock *sk);
extern cm_macroflow_t *cm_classify(struct sock *sk);
extern int cm_make_mflow(cm_flow_t *flow);
extern int cm_mflow_exists(cm_macroflow_t *mf);
extern int cm_add_flow(cm_macroflow_t *mflow, cm_flow_t *flow);
extern int cmflow_request(cm_flow_t *flow);
extern int cm_del_flow(cm_macroflow_t *mflow, cm_flow_t *flow);
extern struct sock *cm_find_sk(struct sock *sk);

extern void cm_log(int event, u32 time, int arg1, int arg2);

#endif








